package geometrie;


/**
 * 
 * Class Matrice qui represent une matrice et ses operations.
 * @author Manuel
 * @author Marcos
 * 
 */

public class Matrice {
	
	/**
	 * La Matrice	 */	
	private double[][] matrice;
	/**Taille des colonnes */
	private int tailleC=0; 
	/**Taille des lignes */
	private int tailleL=0; 
	/**
	 * Constructeur d'une Matrice
	 * @param l Ligne
	 * @param c Colonne 
	 * 
	 * */
	public Matrice(int l,int c){
		matrice = new double[l][c];
		tailleC = c;
		tailleL = l;
	}
	/**
	 * Constructeur d'une Matrice
	 * @param m[][] Tableau x Tableau
	 * 
	 * */
	public Matrice(int l, int c, double[][] m){
		matrice = m;
		tailleC = c;
		tailleL = l;
	}
	/**
	 * Constructeur d'une Matrice[4][1] a partir d'un Point
	 */
	
	public Matrice(Point p){
		matrice = new double[4][1];		
		matrice[0][0] = p.getXCartesien();
		matrice[1][0] = p.getYCartesien();
		matrice[2][0] = p.getZCartesien();
		matrice[3][0] = 1;
		tailleC = 1;
		tailleL = 4;
	}
	
	/**
	 * Obtenir la valeur de la Taille de Colonne
	 * @return La taille de la colonne 
	 * 
	 * */
	public int getTailleC() {
		return tailleC;
	}
	/**
	 * Obtenir la valeur de la Taille de Ligne
	 * @return La taille de la Ligne 
	 * 
	 * */
	public int getTailleL() {
		return tailleL;
	}
	/**
	 * Etablir la valeur du index avec colonne et ligne donnee
	 * @param c l'index de la colonne
	 * @param l l'index de la ligne 
	 * @param valeur la valeur donnee 
	 * 
	 * */
	public void setAt(int l, int c, double valeur){
		if(l >= tailleL || c >= tailleC)
			throw new IndexOutOfBoundsException();
		else
			matrice[l][c] = valeur;
	}
	
	/**
	 * Obtenir la valeur du index avec colonne et ligne donne
	 * @param c l'index de la colonne
	 * @param l l'index de la ligne 
	 * @return la valeur sur l'index donnee
	 * 
	 * */	
	public double getAt(int l, int c){
		if(l >= tailleL || c >= tailleC)
			throw new IndexOutOfBoundsException();
		else
			return matrice[l][c];
	}
	/**
	 * Couper la Matrice de taille L x C
	 * @param l ligne
	 * @param c Colonne
	 * @return Matrice
	 */
	public double[][] getMatrice(int l, int c){
		double[][] m = new double[l][c];
		for(int i = 0; i < l; i++)
			for(int j = 0; j < c;j++)
				m[i][j] = getAt(i,j);
		return m;
	}
	/**
	 * Obtenir la Determinante d'une Matrice 3x3
	 * @param A Matrice pour obtenir la determinante
	 * @return Determinante de matrice A
	 */
	public static double determinant(Matrice A){
		//On a besoin soulement de la calculer pour une Matrice 3x3
		return A.getAt(0,0)*A.getAt(1,1)*A.getAt(2,2)+A.getAt(0,1)*A.getAt(1,2)*A.getAt(2,0)+A.getAt(0,2)*A.getAt(1,0)*A.getAt(2,1)-(A.getAt(0,2)*A.getAt(1,1)*A.getAt(2,0)+A.getAt(0,1)*A.getAt(1,0)*A.getAt(2,2)+A.getAt(0,0)*A.getAt(1,2)*A.getAt(2,1));
	}
	
	/**
	 * Obtenir la inverse de une Matrice
	 * @param m Matrice
	 * @return l'inverse 
	 * @throws Exception Erreur de taille
	 */
	public static Matrice inverse(Matrice m) throws Exception{
		if(m.getTailleL() > 3)
			throw new Exception("Erreur de taille...");
		else{				
			Matrice matrice = new Matrice(3, 3);
			double detM = Matrice.determinant(m);
			int n = 3;
			//Parcourrir la matrice
			for(int i = 0; i < 3;i++)
				for(int j = 0; j < 3; j++){
					//Ajouter la valeur a la Transposee de la Matrice
					double valeur = m.getAt((i+1)%n,(j+1)%n)*m.getAt((i+2)%n,(j+2)%n) - m.getAt((i+2)%n,(j+1)%n)*m.getAt((i+1)%n,(j+2)%n) ;
					matrice.setAt(j, i, valeur);
				}			
			//Inverse (A) = 1/det(A) * Transposee(A)
			return Matrice.scalaire(matrice, 1/detM);
		}
	}
	/**
	 * Obtenir la multiplication de la Matrice avec un Scalaire
	 * @param m Matrice
	 * @param s Scalaire
	 * @return s * M
	 */
	public static Matrice scalaire(Matrice m, double s){
		Matrice matrice = new Matrice(m.getTailleL(), m.getTailleC());
		for(int i = 0; i < m.getTailleL(); i++)
			for(int j = 0; j < m.getTailleC(); j++){
				matrice.setAt(i,j, s * m.getAt(i,j));
			}
		return matrice;
	}
	/**
	 * Executer la multiplication avec deux matrices	
	 * @param m1 La Matrice 1
	 * @param m2 La Matrice 2
	 * @return Une Matrice Produit (m1 x m2)
	 * @throws Exception "Erreur de taille"
	 */
	public static Matrice multMatrice(Matrice m1, Matrice m2) throws Exception{
		if(m1.getTailleC() == m2.getTailleL()){
			Matrice m = new Matrice(m1.getTailleL(),m2.getTailleC());
			for(int k = 0; k < m1.getTailleL();k++)
				for(int i = 0; i < m2.getTailleC();i++)
					for(int j = 0; j < m1.getTailleC();j++){
						m.setAt(k, i, m.getAt(k,i) + m1.getAt(k,j) * m2.getAt(j,i));
					}
			return m;
		}
		else
			throw new Exception("Erreur de taille");				
	}
	/**
	 * Faire le toString pour la class Matrice avec
	 * les donnes dans le Tableau x Tableau
	 * @return String avec les donnes sur le Tableau x Tableau
	 */
	public String toString(){
		StringBuffer sb = new StringBuffer();
		for(int i = 0; i<tailleL;i++){
			for(int j = 0; j<tailleC;j++)
				sb.append(new String("" + matrice[i][j]) + " ");
			sb.append("\n");
		}
		return sb.toString();
	}	
}
